iT邦幫忙

2026 iThome 鐵人賽

DAY 6
0
JavaScript

不要再說你不會後端!30 天 Node.js 降維打擊指南 系列 第 6 篇

Day 6:事件驅動架構與 EventEmitter 的廣播魔法

  • 分享至 

  • xImage
  •  

Node.js 之所以能如此靈活地處理非同步操作與龐大的資料流,底層最核心的機制就是事件驅動架構 (Event-Driven Architecture)。

在前幾天的實作中,你其實已經默默使用過它了。例如 Day 4 的 server.listen() 或 Day 5 的 readStream.on('end', ...),這些能在「某個特定時間點」自動觸發函式執行的機制,背後全都是靠 Node.js 的內建核心模組:events 裡的 EventEmitter 在撐腰。


1. 核心概念:廣播電台與收音機

傳統的程式碼是「指令式」的:你呼叫函式 A,接著呼叫函式 B,一步一步往下走。
事件驅動架構則是「訂閱與發佈 (Pub/Sub)」模式,就像一個廣播電台:

  • 發佈者 (Emitter):負責在特定時刻廣播一個「事件名稱」與「相關資料」(就像電台發送訊號)。
  • 訂閱者 (Listener):負責監聽特定的「事件名稱」,一旦聽到廣播,就立刻執行對應的動作(就像收音機轉到特定頻道播放音樂)。

這讓程式碼的模組之間達到了極致的「低耦合」,發佈者不需要知道是誰在監聽,它只管大喊一聲「任務完成啦!」就好。


2. 實作練習:打造自己的事件中心

我們來模擬一個真實後端場景:當使用者「註冊成功」時,系統需要同時做三件事:寫入日誌、發送歡迎 Email、發送發票。如果用傳統寫法,註冊函式會變得無比肥大;但用 EventEmitter,程式碼會變得異常優雅。

在 hello-node 資料夾下,新增一個 event-demo.js,輸入以下程式碼:

import { EventEmitter } from 'events';
import dayjs from 'dayjs'; // 沿用 Day 2 安裝的套件

// 建立一個繼承自 EventEmitter 的自定義類別 (實務上常用的做法)
class UserSystem extends EventEmitter {
    registerUser(username, email) {
        console.log(`\n[系統] 正在處理 ${username} 的註冊程序...`);
        // 模擬寫入資料庫的時間
        setTimeout(() => {
            console.log(`[系統] ${username} 註冊成功!`);
            
            // 關鍵:註冊完成後,對全域廣播一個名為 'userRegistered' 的事件
            // 並將使用者的資料當作「包裹」一起廣播出去
            this.emit('userRegistered', { username, email, time: dayjs().format('HH:mm:ss') });
            
        }, 1000);
    }
}

// 實例化我們的系統
const myApp = new UserSystem();

// ==========================================
//  訂閱者 1:負責寫入安全日誌
myApp.on('userRegistered', (userData) => {
    console.log(` [日誌系統] 記錄:${userData.username} 於 ${userData.time} 註冊。`);
});

//  訂閱者 2:負責發送 Email
myApp.on('userRegistered', (userData) => {
    console.log(` [信件系統] 正在發送歡迎信至:${userData.email}`);
});

//  訂閱者 3:負責發放新手禮包 (使用 .once 只會觸發一次,就算重複註冊也不會重複發放)
myApp.once('userRegistered', (userData) => {
    console.log(` [獎勵系統] 已發放新手大禮包給:${userData.username}`);
});
// ==========================================

console.log(" 伺服器啟動,等待使用者註冊...");

// 模擬兩次使用者註冊
myApp.registerUser("Bibo", "bibo@example.com");

setTimeout(() => {
    myApp.registerUser("Bibo_分身", "bibo2@example.com");
}, 3000);

3. 執行與觀察魔法

在終端機執行 node event-demo.js。

你會觀察到:

  1. 當 Bibo 註冊成功時,日誌、信件、獎勵系統同時且自動被觸發了。
  2. 三秒後 Bibo_分身 註冊時,日誌與信件系統依然正常運作,但「獎勵系統」因為使用了 .once(),所以沒有再次觸發。
  3. image

上一篇
Day 5:Stream 與 Buffer 的記憶體防禦戰
下一篇
Day 7:第一階段總結與實作(批次檔案分析 CLI 工具)
系列文
不要再說你不會後端!30 天 Node.js 降維打擊指南 共 16 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言